<?php


namespace app\common;

use Elasticsearch\ClientBuilder;

class Esclient
{

    protected static $init_es = null;


    public static function init()
    {
        if (empty(self::$init_es)) {
            $config = config('esdb.');

            self::$init_es = ClientBuilder::create()
                ->setSSLVerification(false)
                ->setHosts([$config['host'] . ':' . $config['port']])
                ->setBasicAuthentication($config['user'], $config['pass'])
                ->build();
        }
        return self::$init_es;
    }

    // 统计查询
    public static function sum($options): array
    {
        if (!$options['body']['aggs']) {
            return [];
        }

        $param = [
            'index' => $options['index'],
            'body'  => ['query' => ['match_all' => new \stdClass], 'aggs' => $options['body']['aggs'], 'track_total_hits' => true],

        ];
        $param['body']['query'] = ['bool' => self::parseWhere($options['where'])];
        $response = self::init()->search($param);
        $result = [
            'total'      => $response['hits']['total']['value'], //总条数
            'sum' => $response['aggregations']['total_sum']['value'], //总页数
        ];
        return $result;
    }

    /**
     * 查询多条
     * @param $options
     * @param bool $is_all
     * @return mixed
     */
    public static function search($options,$is_all = false)
    {
        $options  = self::parseParam($options);
        $response = self::init()->search($options);
        return [
            'data'       => $is_all ? $response['hits']['hits'] : array_column($response['hits']['hits'], '_source'),
            'total'      => $response['hits']['total']['value'], //总条数
            'total_page' => isset($options['size']) && $options['size'] > 0 ? ceil($response['hits']['total']['value'] / $options['size']) : 0, //总页数
        ];
    }

    /**
     * 查询单条
     * @param $options
     * @return mixed
     */
    public static function find($options)
    {
        $options  = self::parseParam($options);
        $response = self::init()->search($options);
        $data     = array_column($response['hits']['hits'], '_source');
        return isset($data[0]) ? $data[0] : array();
    }

    /**
     * 聚合查询
     * @param $options
     * @return array
     */
    public static function searchGroup($options)
    {
        $options  = self::parseParam($options);
        $response = self::init()->search($options);
        $data     = isset($response['aggregations'][$options['index']]['buckets']) ? $response['aggregations'][$options['index']]['buckets'] : [];
        $result   = [
            'data'  => $data,
            'total' => count($data), //总条数
        ];
        return $result;
    }

    /**
     * 修改
     * @param $params
     */
    public static function update($params)
    {
        $response = self::init()->update($params);
        if (isset($response['_shards']['failed']) && $response['_shards']['failed'] == 0) {
            return true;
        }
        return false;
    }

    /**
     * 插入数据
     * @param $index_name
     * @param $id
     * @param $data
     * @return mixed
     */
    public static function insert($index_name, $id, $data)
    {
        $params = [
            'index' => $index_name,
            'id'    => $id,
            'body'  => $data
        ];

        $response = self::init()->index($params);
        if (isset($response['_shards']['failed']) && $response['_shards']['failed'] == 0) {
            return true;
        }
        return false;
    }


    /**
     * 删除
     * @param $index
     * @param $id
     * @return mixed
     */
    static public function delete($index, $id)
    {
        $params = [
            'index' => $index,
            'id'    => $id
        ];

        $response = self::init()->delete($params);
        if (isset($response['_shards']['failed']) && $response['_shards']['failed'] == 0) {
            return true;
        }
        return false;
    }


    /**
     * 组合where条件
     * @param $options
     * @return array
     */
    protected static function parseParam($options): array
    {
        $param = [
            'index' => $options['index'],
            'body'  => ['query' => ['match_all' => new \stdClass], 'track_total_hits' => true],
        ];

        if (isset($options['from'])) {
            $param['from'] = intval($options['from']);
        }

        if (isset($options['size'])) {
            $param['size'] = intval($options['size']);
        }

        if (!empty($options['where'])) {
            $param['body']['query'] = ['bool' => self::parseWhere($options['where'])];
        }

        if (!empty($options['sort'])) {
            $param['body']['sort'] = self::parseOrder($options['sort']);
        }

        if (!empty($options['group'])) {
            $param['body']['aggs'][$param['index']]['terms']['field'] = $options['group'];

            if (!empty($options['field'])) {
                foreach ($options['field'] as $key => $value) {
                    preg_match('/(\w+)\((\w+)\)/', is_numeric($key) ? $value : $key, $match);
                    if (isset($match[1]) && in_array($match[1], ['sum', 'max', 'min', 'avg'])) {
                        $param['body']['aggs'][$param['index']]['aggs'][$value][$match[1]]['field'] = $match[2];
                    }
                }
            }

            //分页
            if (!empty($param['size'])) {
                $param['body']['aggs'][$param['index']]['terms']['size']                          = 65536;
                $param['body']['aggs'][$param['index']]['aggs']['bucket_truncate']['bucket_sort'] = ['from' => $param['from'], 'size' => $param['size']];
            }

            $param['size'] = 0;
        }
        return $param;
    }

    protected static function parseWhere(array $where): array
    {
        $param = [];
        foreach ($where as $k => $v) {
            switch ($v[1]) {
                case '=':
                    $param['must'][]['match'][$v[0]] = $v[2];
                    break;
                case 'IN':
                    $param['must'][]['terms'][$v[0]] = $v[2];
                    break;
                case 'NOT IN':
                    $param['must_not'][]['terms'][$v[0]] = $v[2];
                    break;
                case 'LIKE':
                    $param['must'][]['wildcard'][$v[0]] = str_replace('%', '*', $v[2]);
                    break;
                case 'NOT LIKE':
                    $param['must_not'][]['wildcard'][$v[0]] = str_replace('%', '*', $v[2]);
                    break;
                case '>':
                    $param['filter']['range'][$v[0]]['gt'] = $v[2];
                    break;
                case '<':
                    $param['filter']['range'][$v[0]]['lt'] = $v[2];
                    break;
                case '>=':
                    $param['filter']['range'][$v[0]]['gte'] = $v[2];
                    break;
                case '<=':
                    $param['filter']['range'][$v[0]]['lte'] = $v[2];
                    break;
                case '<>':
                    $param['must_not'][]['terms'][$v[0]] = $v[2];
                    break;
                case 'BETWEEN':
                    [$min, $max] = explode(',', $v[2], 2);
                    $param['filter'][] = [
                        'range' => [
                            $v[0] => [
                                'gte' => $min,
                                'lte' => $max,
                            ]
                        ]
                    ];
                    break;
                case 'NOT BETWEEN':
                    [$min, $max] = explode(',', $v[2], 2);
                    $param['filter'][] = [
                        'range' => [
                            $v[0] => [
                                'gte' => $min,
                                'lte' => $max,
                            ]
                        ]
                    ];
                    break;
                case 'NULL':
                    $param['must'][] = [
                        'missing' => ['field' => $v[0]]
                    ];
                    break;
                case 'NOT NULL':
                    $param['must'][] = [
                        'exists' => ['field' => $v[0]]
                    ];
                    break;
                default:
                    $param['must'][]['match'][$v[0]] = $v[2];
                    break;
            }
        }

        return $param;
    }

    protected static function parseLimit($limit): array
    {
        $limits = explode(',', $limit);
        return [
            'from' => isset($limits[1]) ? $limit[0] : 0,
            'size' => $limits[1] ?? $limits[0],
        ];
    }


    protected static function parseOrder($order): array
    {
        $sort = [];
        foreach ($order as $key => $value) {
            if (is_int($key)) {
                $sort[$value] = 'asc';
            } else {
                $sort[$key] = $value;
            }
        }
        return $sort;
    }
}
